package com.small.nacos.client.config.impl;

import small.common.core.constants.CommonConstants;
import small.common.crypto.util.MD5Util;
import com.small.nacos.api.config.listener.AbstractSharedListener;
import com.small.nacos.api.config.listener.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Author zhoujin
 * @Date 2022/11/24 19:27
 */
public class CacheData {

    private final static Logger LOGGER = LoggerFactory.getLogger(CacheData.class);

    //任务id
    private int taskId;

    private final String name;

    public final String dataId;

    public final String group;

    public final String tenant;

    //内容
    private volatile String content;

    //配置类型
    private String type;

    //配置md5
    private volatile String md5;

    //最后一次更新时间
    private long localConfigLastModified;

    //是否首次初始化
    private volatile boolean isInitializing = true;

    //是否使用本地配置
    private volatile boolean isUseLocalConfig = false;

    //监听器
    private final CopyOnWriteArrayList<ManagerListenerWrap> listeners;

    public CacheData(String name, String dataId, String group, String tenant) {
        listeners = new CopyOnWriteArrayList<ManagerListenerWrap>();
        this.name = name;
        this.dataId = dataId;
        this.group = group;
        this.tenant = tenant;
        //从本地配置文件加载配置
        this.content = loadCacheContentFromDiskLocal(name, dataId, group, tenant);
        this.md5 = getMd5String(content);
    }


    public void addListener(Listener listener) {
        if (null == listener) {
            throw new IllegalArgumentException("listener is null");
        }

        ManagerListenerWrap wrap = new ManagerListenerWrap(listener, md5, content);
        if (listeners.addIfAbsent(wrap)) {
            LOGGER.info("[{}] [add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", name, tenant, dataId, group,
                    listeners.size());
        }
    }

    private String loadCacheContentFromDiskLocal(String name, String dataId, String group, String tenant) {
        String content = LocalConfigInfoProcessor.getFailover(name, dataId, group, tenant);
        content = (null != content) ? content : LocalConfigInfoProcessor.getSnapshot(name, dataId, group, tenant);
        return content;
    }

    public static String getMd5String(String config) {
        if (null == config) {
            return CommonConstants.NULL;
        }
        return MD5Util.md5Hex(config, CommonConstants.ENCODE);
    }

    public int getTaskId() {
        return taskId;
    }

    public void setTaskId(int taskId) {
        this.taskId = taskId;
    }

    public String getContent() {
        return content;
    }
    //设置配置内容和密钥
    public void setContent(String content) {
        this.content = content;
        this.md5 = getMd5String(this.content);
    }

    public long getLocalConfigLastModified() {
        return localConfigLastModified;
    }

    public void setLocalConfigLastModified(long localConfigLastModified) {
        this.localConfigLastModified = localConfigLastModified;
    }

    public boolean isInitializing() {
        return isInitializing;
    }

    public void setInitializing(boolean initializing) {
        isInitializing = initializing;
    }

    public boolean isUseLocalConfig() {
        return isUseLocalConfig;
    }

    public void setUseLocalConfig(boolean useLocalConfig) {
        isUseLocalConfig = useLocalConfig;
    }

    public String getMd5() {
        return md5;
    }

    public String getTenant() {
        return tenant;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public void checkListenerMd5() {
        for (ManagerListenerWrap wrap : listeners) {
            if (!md5.equals(wrap.lastCallMd5)){
                safeNotifyListener(dataId, group, content, type, md5, wrap);
            }
        }
    }

    private void safeNotifyListener(String dataId, String group, String content, String type, String md5, ManagerListenerWrap listenerWrap) {
        final Listener listener = listenerWrap.listener;
        Runnable job = new Runnable(){
            @Override
            public void run() {
                if (listener instanceof AbstractSharedListener){
                    ((AbstractSharedListener) listener).fillContext(dataId, group);
                }
                listener.receiveConfigInfo(content);
                listenerWrap.lastCallMd5 = md5;
                listenerWrap.lastContent=content;
            }
        };
        if (listener.getExecutor() != null){
            listener.getExecutor().execute(job);
        }else {
            job.run();
        }

    }

    private static class ManagerListenerWrap{

        final Listener listener;
        //最后一次md5
        String lastCallMd5 = "";

        //最后一次配置内容
        String lastContent = null;

        ManagerListenerWrap(Listener listener) {
            this.listener = listener;
        }

        ManagerListenerWrap(Listener listener, String md5) {
            this.listener = listener;
            this.lastCallMd5 = md5;
        }

        ManagerListenerWrap(Listener listener, String md5, String lastContent) {
            this.listener = listener;
            this.lastCallMd5 = md5;
            this.lastContent = lastContent;
        }

        @Override
        public boolean equals(Object obj) {
            if (null == obj || obj.getClass() != getClass()) {
                return false;
            }
            if (obj == this) {
                return true;
            }
            ManagerListenerWrap other = (ManagerListenerWrap) obj;
            return listener.equals(other.listener);
        }

        @Override
        public int hashCode() {
            return super.hashCode();
        }
    }

}
